!pr1
Generic Conversion Routines................Bob Sander-Cederlof

I may have written hundreds of different versions of the elementary I/O conversion routines.  The first few would have been for the IBM 704, back in college days.  Then there was the G-15, the 1620, the 3100, the 3300, the 6600, the 1700, the 8090, the 960, the 980, the 990, and so on.  Don't worry of those numbers don't mean anything to you.  They are the "names" of computers out of the past, not micro chips.

What I am talking about is writing programs which convert input decimal characters representing decimal numbers into internal binary form, and the converse operation of converting binary numbers into decimal form.  We have published several variations of both in previous newsletters, but I have some special ones to present here.

There are many variations of the basic routines, and that is one reason I have written so many.  Thinking just of the output conversions (binary to decimal):

   *  Convert to a string in memory, or print it out.
   *  Number of bytes in binary number.
   *  Supply leading zeroes or blanks or neither.
   *  Integer, fraction, floating point, or fixed point.
   *  Signed or unsigned.

The routine I set out to write today works with unsigned integers, prints out the resulting characters rather than storing them in a string, and does not print any leading zeroes or blanks.  I wrote it to work with two-byte values, between 0 adn 65535.  As an added feature, I indicated in the comments how to expand it to work with larger values.

Lines 1800-2080 in the listing comprise the output conversion routine.  I divide the number by ten, saving the remainder as the least significant digit; the quotient becomes the new number, so I repeat the process until the quotient is zero.  Then the digits, which were all saved on the 6502 stack, are popped back off and printed.

Line 1810 starts the digit counter at 0, and line 1950 increments the counter each time a new digit is pushed onto the stack.  Lines 2020-2060 pull the digits off the stack and print them in reverse order.

Lines 1970-2000 test the quotient:  if it is non-zero, another division is performed; if not, we are ready to print the result.  This is one place where you need to add code if your input values are larger than two bytes, as I indicated in line 1980.  By the way, since we do one division before testing, an input value of zero will print as "0". 

Lines 1830-1930 divide the input value by ten.  It may look like I am dividing by five, but remember 5 = 10/2.  I did more fiddling than analyzing in this loop, but it really does work.  Line 1840 sets the loop count to 16, the number of bits in two bytes.  If you want to convert three-byte values, change the 16 to 24.  The loop needs to be executed once for each bit in the input value.  If you are going to have values longer than two bytes, you also need to add more ROL instructions between lines 1880 and 1900, as indicated in my comment line 1890.  If you were to need a three byte conversion routine, you could just remove the "*--" from the front of lines 1890 and 1980, and chane line 1840 to LDY #24.

Notice that this subroutine is very short, and fairly fast.  I have an idea that some of you will think of ways to make it shorter and faster; if you do, try to keep it easily modifiable for the number of bytes in values.

Next I wrote a program to convert from a decimal string into binary, lines 1290-1720.  It is also set up for unsigned two-byte integer values, with comments indicating how to modify it for longer values.  I have written shorter routines before, but this one makes extension to longer values easy and tests for overflow.

The string is assumed to be in ASCII, with high bits = 1, starting at $0200, and terminated by any non-digit.  It just so happens that these are just the conditions you usually find in an Apple, because almost all input routines use the buffer at $0200.  Woz started it, and we all followed Woz.

Lines 1300-1330 clear the value, as well as starting the buffer index at zero.  The rest of the routine scans through the digits.  Each time the current value is multiplied by ten, and the next digit added.  If at any point an overflow is detected (a value too large for the number of bytes) the routine rings the bell and quits.  You can use some other error indication, and probably should, such as printing "NUMBER TOO LARGE".

In order to multiply by ten, I set aside another storage area equal in length to the value accumulator.  At line 1380 the new digit is saved in the Y-register.  The accumulated value at this point is in XH and XL.  Lines 1390-1480 form the value*4 in SH and XL, leaving the original value in XH and SL.  (Yes, they are criss-crossed.)  Lines 1410-1420 show how you would extend this portion to longer values.

Lines 1490-1610 add value*4 to value to get value*5, and then double the result to get value*10.  Again, lines 1530-1550 show how to extend the value.  Lines 1630-1700 add in the new digit, and the comments show how to extend to longer values.

The top level routine in lines 1130-1270 is just a test routine.  It calls the monitor line input routine.  If you type an empty line, it will stop.  Otherwise it calles the input conversion routine, prints the resulting value in hexadecimal, and converts it back to decimal with the output conversion routine.
